package com.zhao.common.util;

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.Random;

public class RSAUtil {
    public static final String SIGNATURE_INSTANCE = "SHA1WithRSA";
    public static final String KEY_ALGORITHM_RSA = "RSA";
    public static final String CHARSET_UTF8 = "UTF-8";
    public static final int KEY_SIZE = 1024;


    public static KeyPair generateKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA);
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.generateKeyPair();
    }

    public static KeyPair generateKeyPair() throws Exception {
        return generateKeyPair(KEY_SIZE);
    }

    public static PublicKey getPublicKey(String publicKeyStr) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr.getBytes(CHARSET_UTF8));
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_RSA);
        return keyFactory.generatePublic(keySpec);
    }

    public static PrivateKey getPrivateKey(String privateKeyStr) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr.getBytes(CHARSET_UTF8));
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_RSA);
        return keyFactory.generatePrivate(keySpec);
    }

    public static String key2String(Key key) throws Exception {
        return new String(Base64.getEncoder().encode(key.getEncoded()), CHARSET_UTF8);
    }

    public static byte[] sign(byte[] content, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
        signature.initSign(privateKey);
        signature.update(content);
        return signature.sign();
    }

    public static String sign(String content, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
        Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
        signature.initSign(privateKey);
        signature.update(content.getBytes(CHARSET_UTF8));
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    public static boolean verify(byte[] content, byte[] sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
        signature.initVerify(publicKey);
        signature.update(content);
        return signature.verify(sign);
    }

    public static boolean verify(String content, String sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_INSTANCE);
        signature.initVerify(publicKey);
        signature.update(content.getBytes(CHARSET_UTF8));
        return signature.verify(Base64.getDecoder().decode(sign));
    }

    public static byte[] encrypt(byte[] content, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(content);
    }

    public static byte[] decrypt(byte[] content, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_RSA);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(content);
    }

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();
        String publicKeyStr = key2String(keyPair.getPublic());
        System.out.println(publicKeyStr);
        String privateKeyStr = key2String(keyPair.getPrivate());
        PublicKey publicKey = getPublicKey(publicKeyStr);
        PrivateKey privateKey = getPrivateKey((privateKeyStr));
        final byte[] signatureData = sign(publicKey.getEncoded(), privateKey);
        boolean isVerify = verify(publicKey.getEncoded(), signatureData, publicKey);
        System.out.println(isVerify);
        //
        long year = 360 * 24 * 60 * 60 * 1000L;
        X500Name x500Name = new X500Name("CN=root,OU=单位,O=组织");
        BigInteger serial = BigInteger.probablePrime(32, new Random());
        Date notBefore = new Date();
        Date notAfter = new Date(notBefore.getTime() + year);
        SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(publicKey.getEncoded()));
        X509v3CertificateBuilder builder = new X509v3CertificateBuilder(x500Name, serial, notBefore, notAfter, x500Name, subjectPublicKeyInfo);
        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA").build(privateKey);
        X509CertificateHolder holder = builder.build(contentSigner);
        //
        InputStream is = new ByteArrayInputStream(holder.getEncoded());
        Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(is);
        X509Certificate x509Certificate = (X509Certificate) certificate;
        System.out.println(key2String(x509Certificate.getPublicKey()));
    }
}
